package com.java110.accessControl.adapter.changpai;

import com.alibaba.fastjson.JSONObject;
import com.java110.accessControl.adapter.DefaultAbstractAccessControlAdapt;
import com.java110.core.adapt.ICallAccessControlService;
import com.java110.core.constant.ResponseConstant;
import com.java110.core.factory.MqttFactory;
import com.java110.core.factory.NotifyAccessControlFactory;
import com.java110.core.factory.RedisCacheFactory;
import com.java110.core.service.machine.IMachineService;
import com.java110.core.util.DateUtil;
import com.java110.core.util.SeqUtil;
import com.java110.core.util.StringUtil;
import com.java110.entity.accessControl.HeartbeatTaskDto;
import com.java110.entity.accessControl.UserFaceDto;
import com.java110.entity.cloud.MachineCmdResultDto;
import com.java110.entity.cloud.MachineHeartbeatDto;
import com.java110.entity.machine.MachineDto;
import com.java110.entity.openDoor.OpenDoorDto;
import com.java110.entity.response.ResultDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 厂拍 门禁设备 Mqtt 方式
 */
@Service("changpaiMqttAssessControlProcessAdapt")
public class ChangpaiMqttAssessControlProcessAdapt extends DefaultAbstractAccessControlAdapt {

    private static Logger logger = LoggerFactory.getLogger(ChangpaiMqttAssessControlProcessAdapt.class);
    //public static Function fun=new Function();

    @Autowired
    private IMachineService machineServiceImpl;

    public static final long START_TIME = new Date().getTime() - 1000 * 60 * 60;
    public static final long END_TIME = new Date().getTime() + 1000 * 60 * 60 * 24 * 365;

    public static final String OPEN_TYPE_FACE = "1000"; // 人脸开门

    //平台名称
    public static final String MANUFACTURER = "YLD_";

    public static final String VERSION = "0.2";

    //发布
    private static final String REQUEST_FACE = "changpaimenjin/topic/face/manage/request/SN";
    //接受
    private static final String RESPONSE_FACE = "changpaimenjin/topic/face/manage/response/";


    private static final String UPLOAD_RESPONSE_FACE = "changpaimenjin/topic/face/capture/request/";


    //心跳
    private static final String HEARTBEAT_FACE = "changpaimenjin/topic/face/heart/request/";

    public static final String CACHE_CHANGPAI = "_CACHE_CHANGPAIMENGJIN";

    public static final String SN = "SN";

    public static final String FACE_URL = "ACCESS_CONTROL_FACE_URL";

    public static final String FACE_RESULT = "face_result";

    //图片后缀
    public static final String IMAGE_SUFFIX = ".jpg";

    public static final Map<String, String> resultInfo = new HashMap<>();

    static {
        resultInfo.put("0", "成功，人员已添加或修改");
        resultInfo.put("2", "设备还未实现此功能（早于2020年8月21日的版本）");
        resultInfo.put("3", "给定的数据不正确（如果出现这个响应码，则会有desc对具体错误进行文字描述）");
        resultInfo.put("6", "资源不足。设备内存不够");
        resultInfo.put("16", "人脸重复（在开启了禁止重复人脸注册时可能出现）");
        resultInfo.put("20", "数据录入达到上限。设备人员已满");
        resultInfo.put("21", "记录已经存在。请求消息体中upload_mode为1，而设备已经存在对应id的用户");
        resultInfo.put("22", "记录不存在。请求消息体中upload_mode为2，而设备中不存在对应id的用户");
        resultInfo.put("25", "提取人脸失败。图像中无人脸");
        resultInfo.put("35", "图像解码失败。给定图像不为jpg或png");
        resultInfo.put("36", "图像太大。JPG文件不能大于10M");
        resultInfo.put("37", "归一化失败。只出现在给了错误的normal_image数据");
        resultInfo.put("38", "人脸尺寸太小");
        resultInfo.put("39", "人脸质量太差");
        resultInfo.put("40", "图像中有多个人脸");
        resultInfo.put("41", "图像中人脸不完整");
    }

    @Override
    public void initAssessControlProcess() {
        logger.debug("初始化是配置器");


    }

    @Override
    public ResultDto initAssessControlProcess(MachineDto machineDto) {
        MqttFactory.subscribe("changpaimenjin/topic/face/capture/request/" + machineDto.getMachineCode());
        MqttFactory.subscribe("changpaimenjin/topic/face/manage/response/" + machineDto.getMachineCode());
        MqttFactory.subscribe("changpaimenjin/topic/face/heart/request/" + machineDto.getMachineCode());
        return new ResultDto(ResultDto.SUCCESS, ResultDto.SUCCESS_MSG);
    }

    @Override
    public int getFaceNum(MachineDto machineDto) {
        return 0;
    }

    @Override
    public String getFace(MachineDto machineDto, UserFaceDto userFaceDto) {


        return null;
    }

    @Override
    public ResultDto addFace(MachineDto machineDto, UserFaceDto userFaceDto) {
        JSONObject param = new JSONObject();
        param.put("cmd", "upload person");
        param.put("id", userFaceDto.getUserId());
        param.put("name", userFaceDto.getName());
        param.put("role", 1);
        param.put("kind", 0);
        param.put("reg_image", userFaceDto.getFaceBase64().replaceAll("\n", ""));
        RedisCacheFactory.setValue(userFaceDto.getUserId() + "_upload" + CACHE_CHANGPAI, userFaceDto.getTaskId(), RedisCacheFactory.RESEND_DEFAULT_EXPIRETIME);
        MqttFactory.publish(REQUEST_FACE.replace(SN, machineDto.getMachineCode()), param.toJSONString());
        saveLog(userFaceDto.getTaskId(), machineDto.getMachineId(), REQUEST_FACE.replace(SN, machineDto.getMachineCode()) + "/uploadperson",
                param.toJSONString(), "", "", userFaceDto.getUserId(), userFaceDto.getName());

        return new ResultDto(ResultDto.SUCCESS, "推送成功");
    }

    @Override
    public ResultDto updateFace(MachineDto machineDto, UserFaceDto userFaceDto) {
        HeartbeatTaskDto heartbeatTaskDto = new HeartbeatTaskDto();
        heartbeatTaskDto.setTaskid(userFaceDto.getTaskId());
        heartbeatTaskDto.setTaskinfo(userFaceDto.getUserId());
        deleteFace(machineDto, heartbeatTaskDto);
        addFace(machineDto, userFaceDto);
        return new ResultDto(ResultDto.SUCCESS, "推送成功");
    }

    @Override
    public ResultDto deleteFace(MachineDto machineDto, HeartbeatTaskDto heartbeatTaskDto) {
        JSONObject param = new JSONObject();
        param.put("cmd", "delete person");
        param.put("flag", -1);
        param.put("id", heartbeatTaskDto.getTaskinfo());
        RedisCacheFactory.setValue(heartbeatTaskDto.getTaskinfo() + "_delete" + CACHE_CHANGPAI, heartbeatTaskDto.getTaskid(), RedisCacheFactory.RESEND_DEFAULT_EXPIRETIME);
        MqttFactory.publish(REQUEST_FACE.replace(SN, machineDto.getMachineCode()), param.toJSONString());
        saveLog(heartbeatTaskDto.getTaskid(), machineDto.getMachineId(), REQUEST_FACE.replace(SN, machineDto.getMachineCode()) + "/deleteperson", param.toJSONString(), "", "",
                heartbeatTaskDto.getTaskinfo(), "未知");

        return new ResultDto(ResultDto.SUCCESS, "推送成功");
    }

    @Override
    public ResultDto clearFace(MachineDto machineDto, HeartbeatTaskDto heartbeatTaskDto) {

        return new ResultDto(ResultDto.SUCCESS, "推送成功");
    }

    /**
     * 扫描设备
     *
     * @return
     */
    @Override
    public List<MachineDto> scanMachine() throws Exception {

        MachineDto machineDto = new MachineDto();

        ResultDto resultDto = machineServiceImpl.getMachine(machineDto);

        if (resultDto == null || resultDto.getCode() != ResponseConstant.SUCCESS) {
            logger.error("查询设备信息失败" + machineDto.toString());
            return null;
        }

        List<MachineDto> machineDtos = (List<MachineDto>) resultDto.getData();
        JSONObject param = null;
        for (MachineDto tmpMachineDto : machineDtos) {
            setUiTitle(tmpMachineDto);
        }
        return null;
    }

    @Override
    public void mqttMessageArrived(String taskId, String topic, String data) {

        JSONObject param = JSONObject.parseObject(data);

        String cmd = param.getString("cmd");

        if (topic.startsWith(HEARTBEAT_FACE)) {
            try {
                heartbeat(data, "");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return;
        }
        if ("face".equals(cmd)) {
            openDoorResult(data);
            return;
        }

        if (topic.startsWith(RESPONSE_FACE)) {
            doCmdResultCloud(JSONObject.parseObject(data));
        }

    }

    /**
     * # 状态码，可能的取值：
     * #    0：成功，人员已添加或修改
     * #    2：设备还未实现此功能（早于2020年8月21日的版本）
     * #    3：给定的数据不正确（如果出现这个响应码，则会有desc对具体错误进行文字描述）
     * #    6：资源不足。设备内存不够
     * #	   16：人脸重复（在开启了禁止重复人脸注册时可能出现）
     * #    20：数据录入达到上限。设备人员已满
     * #    21：记录已经存在。请求消息体中upload_mode为1，而设备已经存在对应id的用户
     * #    22：记录不存在。请求消息体中upload_mode为2，而设备中不存在对应id的用户
     * #    25：提取人脸失败。图像中无人脸
     * #    35：图像解码失败。给定图像不为jpg或png
     * #    36：图像太大。JPG文件不能大于10M
     * #    37：归一化失败。只出现在给了错误的normal_image数据
     * #    38：人脸尺寸太小
     * #    39：人脸质量太差
     * #    40：图像中有多个人脸
     * #    41：图像中人脸不完整
     *
     * @param resultCmd
     */
    private void doCmdResultCloud(JSONObject resultCmd) {
        try {
            String taskId = resultCmd.getString("id");
            String cmd = resultCmd.getString("cmd");
            if (StringUtil.isEmpty(cmd)) {
                return;
            }
            if (cmd.startsWith("delete")) {
                taskId = RedisCacheFactory.getAndRemoveValue(taskId + "_delete" + CACHE_CHANGPAI);
            } else if (cmd.startsWith("upload")) {
                taskId = RedisCacheFactory.getAndRemoveValue(taskId + "_update" + CACHE_CHANGPAI);
            }
            if (StringUtil.isEmpty(taskId)) {
                return;
            }
            int code = -1;
            if (!resultCmd.containsKey("code")) {
                code = -1;
            } else {
                code = resultCmd.getIntValue("code");
            }
            String msg = resultCmd.getString("reply") + "说明：" + resultInfo.get(code + "");
            ICallAccessControlService notifyAccessControlService = NotifyAccessControlFactory.getCallAccessControlService();
            MachineCmdResultDto machineCmdResultDto = new MachineCmdResultDto(code, msg, taskId, "", resultCmd.toJSONString());
            notifyAccessControlService.machineCmdResult(machineCmdResultDto);
        } catch (Exception e) {
            logger.error("上报执行命令失败", e);
        }
    }

    @Override
    public void restartMachine(MachineDto machineDto) {
        JSONObject param = JSONObject.parseObject("{\n" +
                "  \"cmd\": \"reboot\"\n" +
                "}");
        MqttFactory.publish(REQUEST_FACE.replace(SN, machineDto.getMachineCode()), param.toJSONString());
        saveLog(SeqUtil.getGeneratorId(), machineDto.getMachineId(), "reboot", param.toJSONString(), "");

    }

    /**
     * {
     * # 命令
     * "cmd": "gpio control",
     * <p>
     * # 端口号
     * # 远程开门固定传1
     * "port": 1,
     * <p>
     * # 控制类型，on: 闭合 off: 断开
     * # 目前只支持闭合，由相机自动断开
     * "ctrl_type": "on",
     * <p>
     * # 控制模式，test: 测试模式 force: 强制模式 scene: 场景模式
     * # 测试模式：用于测试，不产生开闸信息记录；
     * # 强制方式：输出信号将强制输出到设备；
     * # 场景模式：是否输出信号到设备取决于设备是否使能；
     * "ctrl_mode": "force",
     * }
     *
     * @param machineDto 硬件信息
     */
    @Override
    public void openDoor(MachineDto machineDto) {
        JSONObject param = JSONObject.parseObject("{\n" +
                "       \"cmd\": \"gpio control\",\n" +
                "       \"port\": 1,\n" +
                "       \"ctrl_type\": \"on\",\n" +
                "       \"ctrl_mode\": \"force\",\n" +
                "     }");
        MqttFactory.publish(REQUEST_FACE.replace(SN, machineDto.getMachineCode()), param.toJSONString());
        saveLog(SeqUtil.getGeneratorId(), machineDto.getMachineId(), "openDoor", param.toJSONString(), "");

    }


    /**
     * {
     * "body" : {
     * "code" : 101,
     * "face_imgdata" : "/9j/4AAQSkZJRghDk+tAH/2Q==",
     * "face_imgsize" : 27178,
     * "hat" : 1,
     * "matched" : 91,
     * "model_imgdata" : "/9j/4AAQSkZJRgABAQAAKDKUT/2Q==",
     * "model_imgsize" : 6001,
     * "name" : "吴学文",
     * "per_id" : "772020051963050001",
     * "pic_name" : "9f15b229-0422fd6b_1590397611_91_101.jpg",
     * "role" : 0,
     * "sec" : 1590397611,
     * "sequence" : 2085,
     * "sn" : "9f15b229-0422fd6b",
     * "usec" : 778083
     * },
     * "type" : "face_result"
     * }
     *
     * @param data 这个为设备人脸推送协议，请参考设备协议文档
     * @return
     */

    public void openDoorResult(String data) {

        JSONObject paramJson = JSONObject.parseObject(data);
        logger.debug("考勤结果,{}", paramJson.toJSONString());
        if (!paramJson.containsKey("match")) {
            return;
        }
        JSONObject dataObj = paramJson.getJSONObject("match");

        if (!dataObj.containsKey("person_id") || StringUtil.isEmpty(dataObj.getString("person_id"))) {
            return;
        }
        String staffId = dataObj.getString("person_id");
        String staffName = dataObj.getString("person_name");
        String machineCode = paramJson.getString("device_sn");

        ICallAccessControlService notifyAccessControlService = NotifyAccessControlFactory.getCallAccessControlService();
        try {
            MachineDto machineDto = new MachineDto();
            machineDto.setMachineCode(machineCode);
            List<MachineDto> machineDtos = notifyAccessControlService.queryMachines(machineDto);

            if (machineDtos.size() < 0) {
                return;//未找到设备
            }

            if (StringUtil.isEmpty(staffId)) {
                staffId = "-1";
                staffName = "未知";
            }

            OpenDoorDto openDoorDto = new OpenDoorDto();
            String images= "";
            if(paramJson.containsKey("closeup_pic")){
                JSONObject closeup_pic = paramJson.getJSONObject("closeup_pic");
                images =  closeup_pic.getString("data");
            }

            if(StringUtil.isEmpty(images)){
                images = dataObj.getString("image");
            }

            openDoorDto.setFace(images);
            openDoorDto.setUserName(staffName);
            openDoorDto.setHat("1");
            openDoorDto.setMachineCode(machineCode);
            openDoorDto.setUserId(staffId);
            openDoorDto.setOpenId(SeqUtil.getId());
            openDoorDto.setOpenTypeCd(OPEN_TYPE_FACE);
            openDoorDto.setSimilarity(dataObj.containsKey("face_quality") ? dataObj.getString("face_quality") : "0");
            openDoorDto.setIdNumber(dataObj.getString("idCard"));
            openDoorDto.setTel("11111111111");

            freshOwnerFee(openDoorDto);

            notifyAccessControlService.saveFaceResult(openDoorDto);

        } catch (Exception e) {
            logger.error("推送人脸失败", e);
        }

    }

    @Override
    public String httpFaceResult(MachineDto machineDto, String data) {
        ICallAccessControlService notifyAccessControlService = NotifyAccessControlFactory.getCallAccessControlService();
        try {
            JSONObject param = JSONObject.parseObject(data);

            if (param.containsKey("type") && !FACE_RESULT.equals(param.getString("type"))) {
                return new ResultDto(ResponseConstant.SUCCESS, ResponseConstant.SUCCESS_MSG).toString();
            }

            JSONObject body = param.getJSONObject("body");
            OpenDoorDto openDoorDto = new OpenDoorDto();
            openDoorDto.setFace(body.getString("face_imgdata"));
            openDoorDto.setUserName(body.containsKey("name") ? body.getString("name") : "");
            openDoorDto.setHat(body.getString("hat"));
            openDoorDto.setMachineCode(body.getString("sn"));
            openDoorDto.setUserId(body.containsKey("per_id") ? body.getString("per_id") : "");
            openDoorDto.setOpenId(SeqUtil.getId());
            openDoorDto.setOpenTypeCd(OPEN_TYPE_FACE);
            openDoorDto.setSimilarity(body.containsKey("matched") ? body.getString("matched") : "0");


            freshOwnerFee(openDoorDto);

            notifyAccessControlService.saveFaceResult(openDoorDto);

        } catch (Exception e) {
            logger.error("推送人脸失败", e);
        }
        return new ResultDto(ResponseConstant.SUCCESS, ResponseConstant.SUCCESS_MSG).toString();
    }

    @Override
    public String heartbeat(String data, String machineCode) throws Exception {
        JSONObject info = JSONObject.parseObject(data);

        //设备ID
        machineCode = info.getString("device_sn");
        String heartBeatTime = null;
        heartBeatTime = DateUtil.getNow(DateUtil.DATE_FORMATE_STRING_A);
        MachineHeartbeatDto machineHeartbeatDto = new MachineHeartbeatDto(machineCode, heartBeatTime);
        ICallAccessControlService notifyAccessControlService = NotifyAccessControlFactory.getCallAccessControlService();
        try {
            notifyAccessControlService.machineHeartbeat(machineHeartbeatDto);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}
